package JDK常用类库.stream;

import org.junit.Test;

import java.util.*;

/**
 * @Author：russ
 * @Date：2023/8/14
 * @apiNote：
 * Java8新特性 —— Optional类
 * Optional 类主要解决的问题是臭名昭著的空指针异常(NullPointerException)
 *
 * 组合使用：
 * Optional.ofNullable(需要判断的对象).ifPresent(具体操作)
 * 其实和if相比就是显得优雅一些，主要是防止某处没考虑到，忘记if判断，那么后续可能会导致空指针，如果使用Optional的话，那么这个问题能够得到避免。
 * 就像多使用设计模式一样，让自己的代码更加健壮优雅，还是要多使用一些的！当然不能过渡使用！！
 *
 *
 */
public class OptionalTest {

    /**
     * 以前对null的处理
     */
    @Test
    public void test01(){
        String userName = null;
        if(userName != null){
            System.out.println("字符串的长度：" + userName.length());
        }else{
            System.out.println("字符串为空");
        }
    }

    /**
     * Optional对象的创建方式
     * of()：返回一个携带被指定的value，且该value不能为NULL
     * ofNullable()：如果指定的value不为NULL,则返回携带有该value的Optional类实例,否则返回一个value值为NULL的Optional类实例。
     * 创建optional对象，一般用ofNullable()而不用of()
     */
    @Test
    public void test02(){
        try {
            Optional.ofNullable(false).orElseThrow(() -> new Exception("error"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        ArrayList list = new ArrayList();
        try {
            Optional.of(true).orElseThrow(() -> new Exception("ads adsf"));
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 第一种方式：通过of方法, of方法是不支持null的
        Optional<String> op1 = Optional.of("zhangsan");
        //Optional<Object> op2 = Optional.of(null);
        System.out.println(op1);

        // 第二种方式：通过ofNullable方法, 支持null
        Optional<String> op3 = Optional.ofNullable("lisa").filter(e -> e.length() > 4);
        Optional<Object> op4 = Optional.ofNullable(null);
        System.out.println(op3);
        System.out.println(op4);

        // 第三种方式：通过empty方法直接创建一个空的Optional对象
        Optional<Object> op5 = Optional.empty();
        System.out.println(op5);
    }

    /**
     * get(): 如果Optional有值则返回，否则抛出NoSuchElementException异常。get()通常和isPresent()一块使用
     * isPresent()：判断是否包含值，包含值返回true，不包含值返回false
     * orElse(T t)：如果调用对象包含值，就返回该值，否则返回t
     * orElseGet(Supplier s)：如果调用对象包含值，就返回该值，否则返回Lambda表达式的返回值
     * orElseThrow(Supplier<? extends X> exceptionSupplier): 当Optional有值时, 则将其返回; 否则抛出Supplier接口创建的异常
     */
    @Test
    public void test03(){
        Optional<String> op1 = Optional.of("zhangsan");
        Optional<String> op2 = Optional.empty();

        System.out.println(op1.isPresent());//true
        System.out.println(op2.isPresent());//false

        // 获取Optional中的值
        if(op1.isPresent()){
            String s1 = op1.get();
            System.out.println("用户名称:" + s1);
        }

        if(op2.isPresent()){
            System.out.println(op2.get());
        }else{
            System.out.println("op2是一个空Optional对象");
        }

        String s3 = op1.orElse("李四");
        System.out.println(s3);
        String s4 = op2.orElse("王五");
        System.out.println(s4);

        String s5 = op2.orElseGet(() -> {
            return "Hello";
        });
        System.out.println(s5);
    }

    /**
     * 下面都是Optional应用的例子
     */
    @Test
    public void test04(){
        Optional<String> op1 = Optional.of("zhangsan");
        Optional<String> op2 = Optional.empty();
        // 如果存在值, 就做什么
        op1.ifPresent(s -> System.out.println("有值:" + s));
        op1.ifPresent(System.out::println);
    }

    /**
     * 自定义一个方法，将Person对象中的 name 转换为大写并返回
     */
    @Test
    public void test05(){
        Person p = new Person("zhangsan",18);
        Optional<Person> op = Optional.of(p);
        String name = getNameForOptional(op);
        System.out.println("name = " + name);
    }

    /**
     * map() 方法接受一个函数作为参数，该函数将被应用于 Optional 对象中的值。
     * 如果 Optional 对象存在值，则将该值传递给函数进行转换，否则返回一个空 Optional 对象。
     */
    @Test
    public void test06(){
        String str = "Hello";
        Optional<String> optional = Optional.of(str);
        Optional<String> upperCaseOptional = optional.map(String::toUpperCase);
        System.out.println(upperCaseOptional.get()); // HELLO
    }

    /**
     * flatMap() 方法与 map() 方法类似，都接受一个函数作为参数。但是，flatMap() 方法返回的是一个 Optional 类型的值。
     * 如果函数返回的是一个 Optional 对象，则该方法会将其“展开”，否则返回一个空 Optional 对象。
     *
     * eg：首先创建了一个包含字符串 "hello world" 的 Optional 对象。然后，使用 flatMap() 方法将该字符串转换为第一个字符，
     * 并将结果存储到另一个 Optional 对象 result 中。最后，使用 get() 方法获取 result 对象中的值并打印出来。
     */
    @Test
    public void test07(){
        String str = "hello world";
        Optional<String> optional = Optional.of(str);
        Optional<Character> result = optional.flatMap(s -> {
            if (s.length() > 0)
                return Optional.of(s.charAt(0));
            else
                return Optional.empty();
        });
        System.out.println(result.get()); //输出 h
    }

    /**
     * filter()方法可用于判断Optional对象是否满足给定条件，一般用于条件过滤.
     * 在代码中，如果filter()方法中的Lambda表达式成立，filter()方法会返回当前Optional对象值，否则，返回一个值为空的Optional对象。
     * Optional.ofNullable(变量).orElse(默认数值);
     */
    @Test
    public void test08(){
        Integer number = 10;
        Optional<Integer> optional = Optional.ofNullable(number).filter(n -> n % 2 == 0);
        System.out.println(optional.isPresent()); //输出 true
    }

    /**
     * Optional循环遍历集合
     */
    @Test
    public void test09(){
        List<String> list = new ArrayList<>();
        Optional.ofNullable(list).orElse(new ArrayList<>()).forEach(i -> {
            System.out.println(i);
        });
    }

    /**
     * 根据Person对象 将name转换为大写并返回
     * 通过Optional方式实现
     */
    public String getNameForOptional(Optional<Person> op){
        if(op.isPresent()){
            String msg = op.map(Person::getName)
                            .map(String::toUpperCase)
                            .orElse("空值");
            return msg;
        }
        return null;
    }
}
